#ifndef __SceneManagerEnumerator_H__
#define __SceneManagerEnumerator_H__


#include "U2PreRequest.h"
#include "U2SceneManager.h"
#include "U2Singleton.h"
#include "U2IteratorWrappers.h"
#include "U2StringUtil.h"


U2EG_NAMESPACE_BEGIN



/// Factory for default scene manager
class _U2Share U2DefaultSceneManagerFactory : public U2SceneManagerFactory
{
protected:
	void initMetaData(void) const;

public:
	U2DefaultSceneManagerFactory() {}
	~U2DefaultSceneManagerFactory() {}

	/// Factory type name
	static const U2String       FACTORY_TYPE_NAME;
	U2SceneManager* createInstance(const U2String& instanceName);
	void destroyInstance(U2SceneManager* instance);
};


/// Default scene manager
class _U2Share U2DefaultSceneManager : public U2SceneManager
{
public:
	U2DefaultSceneManager(const U2String& name);
	~U2DefaultSceneManager();
	const U2String& getTypeName(void) const;
};


/** Enumerates the U2SceneManager classes available to applications.
@remarks
    As described in the U2SceneManager class, SceneManagers are responsible
    for organising the scene and issuing rendering commands to the
    U2RenderSystem. Certain scene types can benefit from different
    rendering approaches, and it is intended that subclasses will
    be created to special case this.
@par
    In order to give applications easy access to these implementations,
    this class has a number of methods to create or retrieve a U2SceneManager
    which is appropriate to the scene type. 
@par
	SceneManagers are created by U2SceneManagerFactory instances. New factories
	for new types of U2SceneManager can be registered with this class to make
	them available to clients.
@par
	Note that you can still plug in your own custom U2SceneManager without
	using a factory, should you choose, it's just not as flexible that way.
	Just instantiate your own U2SceneManager manually and use it directly.
*/
class _U2Share U2SceneManagerEnumerator : public U2Singleton<U2SceneManagerEnumerator>, public SceneMgrAlloc
{
public:
	/// Scene manager instances, indexed by instance name
	typedef std::map<U2String, U2SceneManager*>         Instances;
	/// List of available scene manager types as meta data
	typedef std::vector<const SceneManagerMetaData*>    MetaDataList;

private:
	/// Scene manager factories
    typedef std::list<U2SceneManagerFactory*>           Factories;
	Factories               mFactories;
	Instances               mInstances;
	/// Stored separately to allow iteration
	MetaDataList            mMetaDataList;
	/// Factory for default scene manager
	U2DefaultSceneManagerFactory  mDefaultFactory;
	/// Count of creations for auto-naming
	unsigned long           mInstanceCreateCount;
	/// Currently assigned render system
	U2RenderSystem*         mCurrentRenderSystem;

public:
    U2SceneManagerEnumerator();
    ~U2SceneManagerEnumerator();

	/** Register a new U2SceneManagerFactory. 
	@remarks
		Plugins should call this to register as new U2SceneManager providers.
	*/
	void addFactory(U2SceneManagerFactory* fact);

	/** Remove a U2SceneManagerFactory. 
	*/
	void removeFactory(U2SceneManagerFactory* fact);

	/** Get more information about a given type of U2SceneManager.
	@remarks
		The metadata returned tells you a few things about a given type 
		of U2SceneManager, which can be created using a factory that has been
		registered already. 
	@param typeName The type name of the U2SceneManager you want to enquire on.
		If you don't know the typeName already, you can iterate over the 
		metadata for all types using getMetaDataIterator.
	*/
	const SceneManagerMetaData* getMetaData(const U2String& typeName) const;

	typedef ConstVectorIterator<MetaDataList> MetaDataIterator;
	/** Iterate over all types of U2SceneManager available for construction, 
		providing some information about each one.
	*/
	MetaDataIterator getMetaDataIterator(void) const;

	/** Create a U2SceneManager instance of a given type.
	@remarks
		You can use this method to create a U2SceneManager instance of a 
		given specific type. You may know this type already, or you may
		have discovered it by looking at the results from getMetaDataIterator.
	@note
		This method throws an exception if the named type is not found.
	@param typeName U2String identifying a unique U2SceneManager type
	@param instanceName Optional name to given the new instance that is
		created. If you leave this blank, an auto name will be assigned.
	*/
	U2SceneManager* createSceneManager(const U2String& typeName, 
		const U2String& instanceName = U2StringUtil::BLANK);

	/** Create a U2SceneManager instance based on scene type support.
	@remarks
		Creates an instance of a U2SceneManager which supports the scene types
		identified in the parameter. If more than one type of U2SceneManager 
		has been registered as handling that combination of scene types, 
		in instance of the last one registered is returned.
	@note This method always succeeds, if a specific scene manager is not
		found, the default implementation is always returned.
	@param typeMask A mask containing one or more SceneType flags
	@param instanceName Optional name to given the new instance that is
		created. If you leave this blank, an auto name will be assigned.
	*/
	U2SceneManager* createSceneManager(SceneTypeMask typeMask, 
		const U2String& instanceName = U2StringUtil::BLANK);

	/** Destroy an instance of a U2SceneManager. */
	void destroySceneManager(U2SceneManager* sm);

	/** Get an existing U2SceneManager instance that has already been created,
		identified by the instance name.
	@param instanceName The name of the instance to retrieve.
	*/
	U2SceneManager* getSceneManager(const U2String& instanceName) const;

	/** Identify if a U2SceneManager instance already exists.
	@param instanceName The name of the instance to retrieve.
	*/
	bool hasSceneManager(const U2String& instanceName) const;

	typedef MapIterator<Instances> SceneManagerIterator;
	/** Get an iterator over all the existing U2SceneManager instances. */
	SceneManagerIterator getSceneManagerIterator(void);

    /** Notifies all SceneManagers of the destination rendering system.
    */
    void setRenderSystem(U2RenderSystem* rs);

    /// Utility method to control shutdown of the managers
    void shutdownAll(void);
    /** Override standard U2Singleton retrieval.
    @remarks
        Why do we do this? Well, it's because the U2Singleton
        implementation is in a .h file, which means it gets compiled
        into anybody who includes it. This is needed for the
        U2Singleton template to work, but we actually only want it
        compiled into the implementation of the class based on the
        U2Singleton, not all of them. If we don't change this, we get
        link errors when trying to use the U2Singleton-based class from
        an outside dll.
    @par
        This method just delegates to the template version anyway,
        but the implementation stays in this single compilation unit,
        preventing link errors.
    */
    static U2SceneManagerEnumerator& getSingleton(void);
    /** Override standard U2Singleton retrieval.
    @remarks
        Why do we do this? Well, it's because the U2Singleton
        implementation is in a .h file, which means it gets compiled
        into anybody who includes it. This is needed for the
        U2Singleton template to work, but we actually only want it
        compiled into the implementation of the class based on the
        U2Singleton, not all of them. If we don't change this, we get
        link errors when trying to use the U2Singleton-based class from
        an outside dll.
    @par
        This method just delegates to the template version anyway,
        but the implementation stays in this single compilation unit,
        preventing link errors.
    */
    static U2SceneManagerEnumerator* getSingletonPtr(void);

};


U2EG_NAMESPACE_END


#endif
